#include "mm.h"
#include "kernel.h"
#include "string.h"
#include "assert.h"

#define ARDS_ADDR 0x7e00

#define VALID_MEMORY_FROM 0x100000

#define PHY_MEMORY_USE_FROM 0xa00000

#define ZONE_VALID 1
#define ZONE_RESERVED 2

physics_memory_info_t g_physics_memory;

physics_memory_map_t g_physics_memory_map;

check_memory_info_t check_memory_info;

void phy_memory_init()
{
    u16 times = *(u16*) ARDS_ADDR;
    check_memory_info.times = times;
    check_memory_info.data = (check_memory_item_t*) (ARDS_ADDR + 2);

    for (size_t i = 0; i < times; i++)
    {
        check_memory_item_t* tmp = check_memory_info.data + i;
        if (tmp->base_addr_low > 0 && tmp->type == ZONE_VALID)
        {
            // 踩坑记录，由于左移运算符优先级比+低，如果不加括号，会导致先计算加，再移位，导致结果不正确
            g_physics_memory.addr_start = (tmp->base_addr_high << 32) + tmp->base_addr_low;
            g_physics_memory.valid_mem_size = (tmp->length_high << 32) + tmp->length_low;
            g_physics_memory.addr_end = g_physics_memory.addr_start + g_physics_memory.valid_mem_size;
        }
    }
    
    if (VALID_MEMORY_FROM != g_physics_memory.addr_start) 
    {
        printk("[%s:%d] no valid physics memory\n", __FILE__, __LINE__);
        return;
    }
    
    g_physics_memory.pages_total = g_physics_memory.addr_end >> 12;
    g_physics_memory.pages_used = 0;
    g_physics_memory.pages_free = g_physics_memory.pages_total - ((PHY_MEMORY_USE_FROM - VALID_MEMORY_FROM) >> 12);

    g_physics_memory_map.addr_base = PHY_MEMORY_USE_FROM;
    g_physics_memory_map.bitmap_buf = (uchar*) 0x500;
    g_physics_memory_map.pages_total = g_physics_memory.pages_free;


    u32 bitmap_len = g_physics_memory_map.pages_total / 8;

    // 下面这个函数很奇怪，无论内存设置为多大，都只能把0填充到一个固定的位置，原因未知
    // 这里可以把memset的返回值改为终止地址，可以打印出来查看，用我的程序看是0x7b61
    // 不论把内存设置为1G，2G，3G，4G，结果都一样，终止地址是0x7b61，很奇怪，没想明白是为什么
    // void* end_addr = memset(g_physics_memory_map.bitmap_buf, 0, bitmap_len);

    // 这里老师偷偷把bitmap_len改成了512，我发现，如果不改是会报错的，也不知道是覆盖了哪段内存
    // 暂时先放着吧，往后再去深究
    memset(g_physics_memory_map.bitmap_buf, 0, 512);
    bitmap_make(&g_physics_memory_map.bitmap, g_physics_memory_map.bitmap_buf, bitmap_len, 0);
}

void* get_free_page()
{
    int64 ret;
    int index = bitmap_scan(&g_physics_memory_map.bitmap, 1);
    assert(index != EOF);

    ret = g_physics_memory_map.addr_base + (index << 12);
    // printk("get_free_page: %x\n", ret);
    return (void*) ret;
}

void free_page(void* p)
{
    int64 addr = (int64) p;
    // 踩坑记录，由于左移运算符优先级比+低，如果不加括号，会导致先计算加，再移位，导致结果不正确
    int index = (addr - g_physics_memory_map.addr_base) >> 12;
    assert(index < g_physics_memory_map.bitmap.length);
    bitmap_set(&g_physics_memory_map.bitmap, index, false);
}

void print_check_memory_info()
{
    u16 times = *(u16*) ARDS_ADDR;
    check_memory_info.times = times;

    check_memory_info.data = (check_memory_item_t*) (ARDS_ADDR + 2);

    printk("=================== memory check info ================\n");
    for (size_t i = 0; i < times; i++)
    {
        check_memory_item_t *item = &check_memory_info.data[i];
        printk("\t %x, %x, %x, %x, %d\n", item->base_addr_high, item->base_addr_low,
            item->length_high, item->length_low, item->type);
    }
    
    printk("=================== memory check info ================\n");


}